<template>
    <div>
        <el-popover placement="bottom" title="任务列表" width="600" trigger="click" v-model="show">
            <el-empty description="无上传文件" v-show="fileList.length == 0"></el-empty>
            <div class="upload-item" v-for="item, index in fileList" :key="index">
                <!-- <MyFileImg :filename="item.fileType" size="60"></MyFileImg> -->
                <div class="flex">
                    <div class="left">
                        <div class="filename">
                            <span>{{ item.filename }}</span>
                            <span class="file-status">{{ item.status }}</span>
                        </div>
                        <div v-show="item.percentage != 100">
                            <el-progress :text-inside="true" :stroke-width="20" :percentage="item.percentage"
                                class="progress"></el-progress>
                        </div>
                        <div v-show="item.percentage == 100" class="file-size">
                            {{ $global.getSize(item.fileSize) }}
                        </div>
                    </div>

                    <!-- <input type="file" ref="fileInputRef" /> -->
                    <!-- <el-button @click="uploadFile">开始上传文件</el-button> -->
                    <div class="icon" v-show="item.percentage != 100">
                        <i class="el-icon-video-pause" @click="stopUpload(item)" v-show="!item.isStop"></i>
                        <i class="el-icon-video-play" @click="countinueUpload(item)" v-show="item.isStop"></i>
                        <!-- <el-button @click="stopUpload" size="mini">暂停上传</el-button>
                        <el-button @click="countinueUpload" size="mini">继续上传</el-button> -->
                    </div>
                    <div class="icon" v-show="item.percentage == 100">
                        <el-tooltip effect="dark" content="清除记录" placement="top">
                            <i class="el-icon-circle-close" @click="deleteHistoryItem(index)"></i>
                        </el-tooltip>
                    </div>
                </div>
            </div>
            <div class="upload" slot="reference">
                <i class="el-icon-sort"></i>
            </div>
        </el-popover>
    </div>
</template>

<script>
import { getItem } from "@/utils/cookieUtil"
import axios from "axios"
import SparkMD5 from "spark-md5"
import MyFileImg from "@/components/my-file-img"
export default {
    name: 'File',
    components: {
        MyFileImg
    },
    data() {
        return {
            // // 进度条
            // percentage: 0,
            // // 已上传完成的分片索引
            // index: -1,
            // // 是否暂停上传
            // isStop: false,
            // 是否展示
            show: false,
            fileList: JSON.parse(localStorage.getItem("upload_history")) || []
        }
    },
    mounted() {
        // 接收传来的上传参数
        this.$bus.$on("busUploadParams", (data) => {
            this.uploadFile(data)
        })
        // 接收下载的参数
        this.$bus.$on("busDownloadParams", (data) => {
            this.downloadFile(data)
        })
    },
    methods: {
        // 停止上传
        stopUpload(item) {
            item.isStop = true
        },
        // 继续上传
        countinueUpload(item) {
            item.isStop = false
            item.index++;
            this.uploadFileFromIndex(item)
        },
        // 上传
        uploadFile(file) {
            let item = {
                filename: file.name,
                fileSize: file.size,
                file: file,
                index: 0,
                isStop: false,
                fileMd5: "",
                percentage: 0,
                status: "计算MD5中"
            }
            this.fileList.push(item)
            // 计算md5
            this.computeMD5(file, item)
        },

        // 从第几个分片开始上传（index从0开始算，index=0算作第一个分片）
        uploadFileFromIndex(item) {
            let _this = this

            let chunkSize = 1 * 1024 * 1024 // 分片大小 5M
            let chunkTotalCount = Math.ceil(item.file.size / chunkSize) // 分片总数

            uploadSliceFile(item)

            // 上传指定索引的分片文件
            async function uploadSliceFile(item) {
                let idx = item.index
                if (idx >= chunkTotalCount) {
                    console.log('文件已上传完成...');
                    // 记录已完成的分片索引
                    item.index = idx
                    item.percentage = 100
                    // 文件夹ID
                    let path = _this.$route.query.path;
                    let folderId;
                    if (path != null) folderId = path.substring(path.lastIndexOf("/") + 1, path.length);
                    else folderId = 0
                    // 发送合并文件请求
                    mergeFragmentFile({
                        item: item,
                        fileMd5: item.fileMd5,
                        filename: item.file.name,
                        chunkTotal: chunkTotalCount,
                        fileType: item.file.type,
                        folderId: folderId,
                        fileSize: item.fileSize,
                    })
                    return
                }

                // 分片开始位置
                let start = idx * chunkSize
                // 分片结束位置
                let end = (start + chunkSize) > item.file.size ? item.file.size : start + chunkSize
                // 对文件分片
                let sFile = new File([item.file.slice(start, end)], `${item.file.name}.${idx}`)

                let formData = new FormData()
                formData.append('file', sFile)
                formData.append('filename', item.file.name)
                formData.append('fileMd5', item.fileMd5)
                formData.append('pId', 0)
                formData.append('chunkIndex', idx)
                formData.append('chunkTotal', chunkTotalCount)
                let res = await _this.$store.dispatch("uploadChunk", formData)
                if (res.success) {
                    if (idx === chunkTotalCount - 1) {
                        // 已经上传完了最后一个分片
                        console.log('上传完成');
                        // 记录已完成的分片索引
                        item.index = idx
                        item.percentage = 100
                        // 文件夹ID
                        let path = _this.$route.query.path;
                        let folderId;
                        if (path != null) folderId = path.substring(path.lastIndexOf("/") + 1, path.length);
                        else folderId = 0
                        // 发送合并文件请求
                        mergeFragmentFile({
                            item: item,
                            fileMd5: item.fileMd5,
                            filename: item.file.name,
                            chunkTotal: chunkTotalCount,
                            fileType: item.file.type,
                            folderId: folderId,
                            fileSize: item.fileSize,
                        })
                    } else {
                        // 上传完成指定索引的分片之后, 更新文件上传进度
                        item.percentage = parseFloat(((idx + 1) / chunkTotalCount * 100).toFixed(1))
                        // 记录已完成的分片索引
                        item.index = idx
                        if (!item.isStop) {
                            // 如果没有点击暂停的话, 再上传下一个索引的分片
                            item.index++;
                            uploadSliceFile(item)
                        }
                    }
                }
            }

            // 发送合并分片文件请求
            async function mergeFragmentFile(data) {
                item.start = "合并文件中"
                console.log("合并文件")
                let res = await _this.$store.dispatch("mergeChunks", data);
                if (res.success) {
                    item.status = "上传成功"
                    _this.$message.success("上传成功")

                    // 刷新文件列表
                    _this.$bus.$emit("refreshFileList")

                    // 刷新用户内存空间
                    _this.$bus.$emit("busSpaceInfo")

                    // 加入上传历史记录
                    localStorage.setItem("upload_history", JSON.stringify(_this.fileList))
                }
                else _this.$global.resError(res)
            }

        },
        // 获取文件md5
        computeMD5(file, item) {
            var blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice,
                chunkSize = 1 * 1024 * 1024,
                chunks = Math.ceil(file.size / chunkSize),
                currentChunk = 0,
                spark = new SparkMD5.ArrayBuffer(),
                fileReader = new FileReader();
            let _this = this;

            fileReader.onload = function (e) {
                console.log('read chunk nr', currentChunk + 1, 'of', chunks);
                spark.append(e.target.result);
                currentChunk++;

                if (currentChunk < chunks) {
                    loadNext();
                } else {
                    let fileMd5 = spark.end()

                    console.log(fileMd5)
                    spark.destroy(); // 释放内存
                    _this.computeMD5Success(file, fileMd5, item)
                }
            };

            fileReader.onerror = function () {
                console.warn('oops, something went wrong.');
            };

            function loadNext() {
                var start = currentChunk * chunkSize,
                    end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;

                fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
            }

            loadNext();

        },
        async computeMD5Success(file, fileMd5, item) {
            this.show = true
            item.fileMd5 = fileMd5
            item.status = "上传中"
            // 文件夹ID
            let path = this.$route.query.path;
            let folderId;
            if (path != null) folderId = path.substring(path.lastIndexOf("/") + 1, path.length);
            else folderId = 0
            // 先检查分片状态
            let res = await this.$store.dispatch("checkChunk", {
                fileMd5: fileMd5,
                filename: file.name,
                fileSize: file.size,
                fileType: file.type,
                folderId: folderId
            })
            if (res.success) {
                // 秒传
                if (res.skipUpload) {
                    item.percentage = 100
                    item.status = "上传成功"
                    this.$message.success("上传成功")
                    this.$bus.$emit("refreshFileList")
                    this.$bus.$emit("busSpaceInfo")
                    // 保存本地记录
                    localStorage.setItem("upload_history", JSON.stringify(this.fileList))
                    return
                }
                // 断点续传
                item.index = res.uploaded.length
                this.uploadFileFromIndex(item)
            }
            else {
                this.$global.resError(res)
            }
        },
        deleteHistoryItem(index) {
            this.fileList.splice(index, 1);
            localStorage.setItem("upload_history", JSON.stringify(this.fileList))
        },
        // 保存本地历史记录
        saveLocalHistory(data) {
            this.fileList.push(data)
            localStorage.setItem("upload_history", JSON.stringify(this.fileList))
        },
        // 下载文件并显示进度条
        downloadFile(data) {
            let item = {
                filename: data.filename,
                fileSize: data.fileSize,
                isStop: false,
                fileMd5: data.fileMd5,
                percentage: 0,
                status: "下载中"
            }
            this.fileList.push(item)
            setTimeout(() => {
                this.show = true
            }, 500);
            // axios.get("/api/file/getFile/" + data.id,
            //     {
            //         responseType: 'blob',
            //         headers: {
            //             token: getItem("token")
            //         }
            //     })
            //     .then((res) => {
            //         if (!res) return
            //         let blob = new Blob([res.data], { type: 'charset=utf-8' }) // 文件类型
            //         let filename = window.decodeURI(res.headers['content-disposition'].split('=')[1])
            //         let url = window.URL.createObjectURL(blob);  // 创建下载链接
            //         let aLink = document.createElement("a");    // 赋值给a标签的href属性
            //         aLink.style.display = "none";
            //         aLink.href = url;
            //         aLink.setAttribute("download", filename);
            //         document.body.appendChild(aLink);   // 将a标签挂载上去
            //         aLink.click();          // a标签click事件
            //         document.body.removeChild(aLink);  // 移除a标签
            //         window.URL.revokeObjectURL(url);   // 销毁下载链接
            //     }).catch(function (error) {
            //         console.log(error);
            //     })

            axios({
                method: 'get',
                url: "/api/file/getFile/" + data.id,
                responseType: 'blob', // 响应类型为二进制流
                headers: {
                    token: getItem("token")
                },
                onDownloadProgress: progressEvent => {
                    // 计算下载进度
                    const progress = Math.round(
                        (progressEvent.loaded * 100) / progressEvent.total
                    );
                    item.percentage = progress
                    if (progress == 100) {
                        item.status = "下载成功"
                        localStorage.setItem("upload_history", JSON.stringify(this.fileList))
                    }
                }
            }).then(res => {
                if (!res) return
                let blob = new Blob([res.data], { type: 'charset=utf-8' }) // 文件类型
                let filename = window.decodeURI(res.headers['content-disposition'].split('=')[1])
                let url = window.URL.createObjectURL(blob);  // 创建下载链接
                let aLink = document.createElement("a");    // 赋值给a标签的href属性
                aLink.style.display = "none";
                aLink.href = url;
                aLink.setAttribute("download", filename);
                document.body.appendChild(aLink);   // 将a标签挂载上去
                aLink.click();          // a标签click事件
                document.body.removeChild(aLink);  // 移除a标签
                window.URL.revokeObjectURL(url);   // 销毁下载链接
            });
        }
    },
    beforeDestroy() {
        this.$bus.$off("busUploadParams")
        this.$bus.$off("busDownloadParams")
    },
}
</script>

<style scoped>
.upload {
    height: 15px;
    width: 15px;
    /* text-align: center; */
    align-items: center;
    justify-content: center;
    display: flex;
    margin-right: 20px;
    background-color: #f1f3f8;
    border-radius: 50%;
    padding: 5px;
    color: #454d5a;
    cursor: pointer;
}

.upload-item {
    display: flex;
    align-items: center;
}

.upload-item:hover {
    background-color: #f9fafd;
}

.progress {
    width: 350px;
    border-radius: 13px;
}

.flex {
    display: flex;
    align-items: center;
    width: 100%;
}

.filename {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 14px;
    color: #03081a;
}

.icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-grow: 1;
}

.icon i {
    font-size: 20px;
    cursor: pointer;
    color: gray;
}

.file-size {
    font-size: 13px;
    color: gray;
}

.left {
    width: 70%;
    margin: 5px;
}

.file-status {
    font-size: 13px;
    color: gray;
    margin-left: 20px;
}
</style>






<!-- <template>
    <div>
        <uploader :options="options" class="uploader-example" @file-added="onFileAdded" @file-success="onFileSuccess"
            @file-error="fileError" :autoStart="false">
            <uploader-unsupport></uploader-unsupport>
            <uploader-drop>
                <p>Drop files here to upload or</p>
                <uploader-btn>select files</uploader-btn>
                <uploader-btn :attrs="attrs">select images</uploader-btn>
                <uploader-btn :directory="true">select folder</uploader-btn>
            </uploader-drop>
            <uploader-list></uploader-list>
        </uploader>
    </div>
</template>
  
<script>
import SparkMD5 from "spark-md5"
export default {
    data() {
        return {
            options: {
                target: '/api/file/uploadChunk',
                chunkSize: '5242880', //每个分片大小，30mb
                forceChunkSize: true, //强制每个分片都小于chunkSize，否则可能出现单个分片过大的情况
                //一个回调函数，也就是第一次不带文件的请求的一个回调，一般后端对应接口需要返回skipUpload【true or false】
                //js中一个函数是可以作为值的
                checkChunkUploadedByResponse: function (chunk, res) {// 服务器分片校验函数，秒传及断点续传基础
                    //需后台给出对应的查询分片的接口进行分片文件验证
                    //这里是可以自定义的，但最好用官方的参数，如果后端判断文件存在，return一个skipUpload = true就行
                    console.log(res)
                    let objMessage = JSON.parse(res);
                    if (objMessage.skipUpload) {
                        return true;
                    }
                    //如果当前分片比已经上传的最后一个分片要大，则允许上传
                    //如果小于等于，因为已经有序存在于服务器，为false不需要重传
                    return (objMessage.uploaded || []).indexOf(chunk.offset + 1) >= 0
                },
                simultaneousUploads: 1, //最大并发数，可以保证分片到达后端是有序
                headers: {
                    // 在header中添加的验证，请根据实际业务来
                    token: "123123123"
                },
            },
            attrs: {
                accept: 'image/*'
            }
        }
    },
    methods: {
        // 选择文件
        onFileAdded(file) {
            // 计算文件md5
            this.computeMD5(file)
        },
        // 计算文件md5
        computeMD5(file) {
            let fileReader = new FileReader();
            let time = new Date().getTime();
            let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
            let currentChunk = 0;
            const chunkSize = 5242880;
            let chunks = Math.ceil(file.size / chunkSize);
            let spark = new SparkMD5.ArrayBuffer();

            file.pause();

            loadNext();

            fileReader.onload = (e => {
                spark.append(e.target.result);
                if (currentChunk < chunks) {
                    currentChunk++;
                    loadNext();
                    // 实时展示MD5的计算进度
                    this.$nextTick(() => {
                        $(`.myStatus_${file.id}`).text('校验MD5 ' + ((currentChunk / chunks) * 100).toFixed(0) + '%')
                    })
                } else {
                    let md5 = spark.end();
                    this.computeMD5Success(md5, file);
                    console.log(`MD5计算完毕：${file.name} \nMD5：${md5} \n分片：${chunks} 大小:${file.size} 用时：${new Date().getTime() - time} ms`);
                }
            });
            fileReader.onerror = function () {
                this.error(`文件${file.name}读取出错，请检查该文件`)
                file.cancel();
            };
            function loadNext() {
                let start = currentChunk * chunkSize;
                let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
                fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
            }
        },
        computeMD5Success(md5, file) {
            // 文件的唯一标识
            file.uniqueIdentifier = md5;
            file.resume();
        },
        // 成功上传文件
        async onFileSuccess(rootFile, file, response, chunk) {
            console.log(rootFile)
            console.log(file)
            console.log(response)
            console.log(chunk)
            console.log("调用合并方法")
            let data = {
                fileMd5: file.uniqueIdentifier,
                filename: file.name,
                chunkTotal: JSON.parse(response).data.totalChunks,
                fileType: file.fileType,
                folderType: file.isFolder
            }
            let res = await this.$store.dispatch("mergeChunks", data)
            console.log(res)
            // this.$axios({
            //     url: 'https://localhost:8081/api/file/upload',
            //     method: 'post',
            // }).then(res => {
            //     console.log(res);
            // });
            // // 成功后触发使用地方的方法 即uploaderSuccess
            // this.$emit('uploaderSuccess');
        },
        // 上传失败
        fileError(rootFile, file, response, chunk) {
            console.log(rootFile)
            console.log(file)
            console.log(response)
            console.log(chunk)
        },
        // 文件进度的回调
        onFileProgress(rootFile, file, chunk) {
            console.log(`上传中 ${file.name}，chunk：${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
        },



    },
}
</script>
  
<style>
.uploader-example {
    width: 880px;
    padding: 15px;
    margin: 40px auto 0;
    font-size: 12px;
    box-shadow: 0 0 10px rgba(0, 0, 0, .4);
}

.uploader-example .uploader-btn {
    margin-right: 4px;
}

.uploader-example .uploader-list {
    max-height: 440px;
    overflow: auto;
    overflow-x: hidden;
    overflow-y: auto;
}
</style> -->